Jx.declare('Jx.code.Operation', {

	$extend: 'Jx.lib.Operation',

	$helpers: {

		'Str': 'Jx.lib.String'

	},

	generatorDefaultPrefix: 'id',

	generator: undefined,

	declaredSymbols: undefined,

	declaredSymbolNames: undefined,

	implementedSymbols: undefined,

	implementedSymbolNames: undefined,

	params: undefined,

	codes: undefined,

	dependencies: undefined,

	dependencyNames: undefined,

	nullFillSymbols: undefined,

	nullFillSymbolNames: undefined,

	firstIndex: 0,

	compiledProperties: undefined,

	compiledPropertyNames: undefined,

	constructor: function(){

		this.generator = {};

		this.declaredSymbols = {};

		this.declaredSymbolNames = [];

		this.codes = [];

		this.params = [];

		this.dependencies = {};

		this.dependencyNames = [];

		this.nullFillSymbols = {};

		this.nullFillSymbolNames = [];

		this.implementedSymbols = {};

		this.implementedSymbolNames = [];

		this.compiledProperties = {};

		this.compiledPropertyNames = [];

		this.$super(arguments);

	},

	onConstruct: function( compiler, code ){

		this.compiler = compiler;

		if (Jx.isString(code)){

			this.source = code;

		}

		this.$super(arguments);

	},

	onDestroy: function(){

		Jx.destruct(

			this.generator,

			this.declaredSymbols,

			this.declaredSymbolNames,

			this.codes,

			this.params,

			this.dependencies,

			this.dependencyNames,

			this.nullFillSymbols,

			this.nullFillSymbolNames,

			this.implementedSymbols,

			this.implementedSymbolNames

		);

		this.$super(arguments);

	},

	createId: function( prefix ){

		prefix = prefix || this.generatorDefaultPrefix;

		var g = this.generator;

		if (!g.hasOwnProperty(prefix)){

			g[prefix] = 0;

		}

		return prefix + ( ++g[prefix] );

	},

	createRegisterSymbol: function( prefix, defaultValue, first, nullFill ){

		return this.registerSymbol(

			this.createId(

				Jx.isString(prefix) ?

					prefix

					:

					'symbol'

			),

			defaultValue,

			first,

			nullFill

		);

	},

	registerSymbol: function( symbolName, defaultValueCode, first, nullFill){

		var index = this.declaredSymbols,

			list = this.declaredSymbolNames,

			nullIndex = this.nullFillSymbols,

			nullList = this.nullFillSymbolNames;

		if (!index.hasOwnProperty(symbolName)){

			if (first === true){

				list.splice( this.firstIndex++, 0, symbolName );

			} else {

				list[list.length] = symbolName;

			}

			index[ symbolName ] = defaultValueCode && Jx.isString(defaultValueCode) ?

				defaultValueCode

				:

				true;

		}

		if (nullFill){

			this.registerNullFillSymbols( symbolName );

		}

		return symbolName;

	},

	registerSymbolIf: function( symbolName, defaultValueCode, first, nullFill ){

		if (!this.declaredSymbols.hasOwnProperty(symbolName)){

			symbolName = this.registerSymbol( symbolName, defaultValueCode, first, nullFill );

		}

		return symbolName;

	},

	registerDependency: function( codeIndex, failValueCode ){

		var index = this.dependencies,

			list = this.dependencyNames,

			symbolName = void(0);

		/* register compiled symbol if not yet registered */
		this.registerSymbolIf( 'compiled', 'session.compiled', true );

		if ( index.hasOwnProperty( codeIndex ) ){

			symbolName = index[ codeIndex ];

		} else {

			if (!failValueCode){

				failValueCode = 'void(0)';

			}

			var codeIndexCode = '"'+this.__Str.escape(codeIndex)+'"';

			index[ codeIndex ] = symbolName = this.createRegisterSymbol(

				'dependency',

				'compiled.hasOwnProperty(' + codeIndexCode + ') ? compiled['+codeIndexCode+'] : '+failValueCode

			);

			list[ list.length ] = codeIndex;

		}

		return symbolName;

	},

	registerSessionSymbol: function( symbolName, defaultValueCode, nullFill ){

		var Str = this.__Str,

			access = Str.escape(symbolName),

			cleanSymbol = Str.dirtyToJsVarname( symbolName ),

			assignValueCode = Jx.isString(defaultValueCode);

		return this.registerSymbolIf(

			Str.dirtyToJsVarname( symbolName ),

			Str.hasDirtyJsVarnameChars( access ) ?

				'"'+access+'" in session ? session["'+access+'"] : '+ (

					assignValueCode ?

						'(session["'+access+'"] = '+defaultValueCode+')'

						:

						'void(0)'

					)

				:

				'session.'+access+(

					assignValueCode ?

						' || (session.'+access+' = '+defaultValueCode+')'

						:

						''

					),

			true,

			nullFill

		);

	},

	registerNullFillSymbols: function(){

		var nullIndex = this.nullFillSymbols,

			nullList = this.nullFillSymbolNames,

			nullCount = nullList.length,

			registered = [],

			count = 0,

			J = Jx;

		for(var symbol, c=-1, l = arguments.length; l--;){

			symbol = arguments[++c];

			if (symbol && J.isString(symbol) && !nullIndex.hasOwnProperty(symbol)){

				nullIndex[

					nullList[ nullCount++ ] = registered[ count++ ] = symbol

				] = true;

			}

		}

		return registered;

	},

	declareSymbols: function( codes ){

		var J = Jx;

		if (!Jx.isArray(codes)){

			codes = [];

		}

		var codeCount = codes.length,

			symbolNames = this.declaredSymbolNames,

			symbols = this.declaredSymbols,

			implementedIndex = this.implementedSymbols,

			implementedList = this.implementedSymbolNames,

			vars = [],

			varCount = 0;

		for(var symbol, defaultValue, c=-1, l = symbolNames.length; l--;){

			symbol = symbolNames[++c];

			if (!implementedIndex.hasOwnProperty(symbol)){

				implementedIndex[

					implementedList[ implementedList.length ] = symbol

				] = true;

				defaultValue = symbols[ symbol ];

				if (J.isString(defaultValue)){

					codes[ codeCount++ ] = 'var ' + symbol + ' = ' + defaultValue+';';

				} else {

					vars[ varCount ++ ] = symbol;

				}

			}


		}

		if (vars.length){

			codes[ codeCount++ ] = 'var ' + vars.join(',') +';';

		}

		return codes;

	},

	declareNullSymbols: function( codes ){

		var J = Jx,

			symbolNames = this.implementedSymbolNames,

			nullIndex = this.nullFillSymbols;

		if (!Jx.isArray(codes)){

			codes = [];

		}

		var codeCount = codes.length;

		for(var symbol, l = symbolNames.length; l--;){

			symbol = symbolNames[ l ];

			if (nullIndex.hasOwnProperty(symbol)){

				codes[ codeCount++ ] = symbol + ' = null;';

			}

		}

		return codes;

	},

	setCompiledProperty: function( name, value ){

		if (Jx.isString(name)){

			var properties = this.compiledProperties,

				names = this.compiledPropertyNames;

			if ( !properties.hasOwnProperty(name) ){

				names[names.length] = name;

			}

			properties[name] = value;

		}

	},

	addParameter: function( name, index ){

		var J = Jx,

			allParams = this.params,

			many = J.isArray(name);

		if ( J.isNumber(index) ){

			if (many){

				name = name.slice(0);

				name.unshift.apply(name, index, 0);

				allParams.splice.apply( allParams, name );

			} else {

				allParams.splice( index, 0, name );

			}

		} else {

			var len = allParams.length;

			if (many){

				allParams.push.apply( allParams, name );

			} else {

				allParams[ len ] = name;

			}

		}


	}


});
